//
//  ShakeView.swift
//  NukeTest
//
//  Created by JackMayx on 2020/7/12.
//  Copyright © 2020 JackMa. All rights reserved.
//

import Foundation
import UIKit


public enum ShakeDirection {
    
    case horizontal
    case vertical

    var startPosition: ShakePosition {
        switch self {
        case .horizontal:
            return .left
        default:
            return .top
        }
    }
}

public enum Vibration {
    case light
    case medium
    case heavy
    
    @available(iOS 10.0, *)
    var hapticFeedback: UIImpactFeedbackGenerator {
        switch self {
        case .heavy:
            return UIImpactFeedbackGenerator(style: .heavy)
        case .medium:
            return UIImpactFeedbackGenerator(style: .medium)
        case .light:
            return UIImpactFeedbackGenerator(style: .light)
        }
    }
}

extension UIView {

    /**
    Shake a view back and forth for the number of times given in the duration specified.
    If the total duration given is 1 second, and the number of shakes is 5, it will use 0.20 seconds per shake.
    After it's done shaking, the completion handler is called, if specified.

    - parameter direction:     The direction to shake (horizontal or vertical motion)
    - parameter numberOfTimes: The total number of times to shake back and forth, default value is 5
    - parameter totalDuration: Total duration to do the shakes, default is 0.5 seconds
    - parameter completion:    Optional completion closure
    */
    @discardableResult
    public func shake(direction: ShakeDirection, numberOfTimes: Int = 5, totalDuration: Float = 0.5, completion: (() -> Void)? = nil) -> UIView? {
        if UIAccessibility.isVoiceOverRunning {
            return self
        } else {
            let timePerShake = Double(totalDuration) / Double(numberOfTimes)
            shake(for: numberOfTimes, position: direction.startPosition, durationPerShake: timePerShake, completion: completion)
            return nil
        }
    }
    
    @available(iOS 10.0, *)
    public func withHapticVibration(_ intesity: Vibration) {
        intesity.hapticFeedback.impactOccurred()
    }
    
    private func shake(for forTimes: Int, position: ShakePosition, durationPerShake: TimeInterval, completion: (() -> Void)?) {
        UIView.animate(withDuration: durationPerShake, animations: { () -> Void in

            switch position.direction {
            case .horizontal:
                self.layer.setAffineTransform( CGAffineTransform(translationX: 2 * position.value, y: 0))
                break
            case .vertical:
                self.layer.setAffineTransform( CGAffineTransform(translationX: 0, y: 2 * position.value))
                break
            }
            }) { (complete) -> Void in
                if (forTimes == 0) {
                    UIView.animate(withDuration: durationPerShake, animations: { () -> Void in
                        self.layer.setAffineTransform(CGAffineTransform.identity)
                        }, completion: { (complete) -> Void in
                            completion?()
                    })
                } else {
                    self.shake(for: forTimes - 1, position: position.oppositePosition, durationPerShake: durationPerShake, completion:completion)
                }
        }
    }

}

public struct ShakePosition {

    let value: CGFloat
    let direction: ShakeDirection

    init(value: CGFloat, direction: ShakeDirection) {
        self.value = value
        self.direction = direction
    }

    var oppositePosition: ShakePosition {
        return ShakePosition(value: (self.value * -1), direction: direction)
    }

    static var left: ShakePosition {
        return ShakePosition(value: 1, direction: .horizontal)
    }

    static var right: ShakePosition {
        return ShakePosition(value: -1, direction: .horizontal)
    }

    static var top: ShakePosition {
        return ShakePosition(value: 1, direction: .vertical)
    }

    static var bottom: ShakePosition {
        return ShakePosition(value: -1, direction: .vertical)
    }
    
}
